# -*- coding: utf-8 -*-
"""
-------------------------------------------------
   File Name：     lexical_checker
   Description :
   Author :       Flyoung
   date：          2023/3/5
-------------------------------------------------
   Change Activity:
                   2023/3/5:
-------------------------------------------------
"""
import re
from typing import List, AnyStr

from compiler.lexical.lib import (
    brackets,
    left_brackets,
    right_brackets,
    bracket_match
)
from compiler.lexical.lib_utils import (
    is_empty_str,
    is_digit,
    is_constant,
    is_reserved_word,
    is_sign,
    is_user_defined
)
from compiler.lexical.tokenizer import Tokenizer


class LexicalChecker:
    """
    词法检查
    """

    @staticmethod
    def is_valid_variable_name(name: AnyStr) -> bool:
        """
        是否为合法的变量名
        :param name:
        :return:
        """
        if sum([
            is_empty_str(name),
            is_constant(name),
            is_sign(name),
            is_reserved_word(name)
        ]) > 0:
            return False
        if is_digit(name[0]):
            return False
        return True

    @staticmethod
    def check_variable_naming(lines: List[AnyStr], log: bool = True):
        """
        检查变量命名
        :param lines:
        :param log: 是否输出日志
        :return:
        """
        t = Tokenizer()
        valid, invalid = [], []
        for line_no, line in enumerate(lines):
            names = t.separate_variables([line])
            for name in names:
                if LexicalChecker.is_valid_variable_name(name):
                    valid.append(name)
                else:
                    if log:
                        idx = line.index(name)
                        template = f"{' ' * idx}{'~' * len(name)}"
                        print(
                            f"[Error] The variable '{name}' at line {line_no + 1} position {idx + 1} is an illegal name")
                        print(line)
                        print(f'{template}\n')
                    invalid.append(name)

        return valid, invalid

    @staticmethod
    def check_bracket_match(lines: List[AnyStr], log: bool = True):
        """
        检查变括号匹配
        :param lines:
        :param log:
        :return:
        """

        def handle_bracket_match_error_log(bracket_item_):
            if log:
                column_number_ = bracket_item_['column_number']
                line_number_ = bracket_item_['line_number']
                line_ = bracket_item_['line']
                template = f"{' ' * column_number_}~"
                print(
                    f"[Error] The bracket at line {line_number_ + 1} position {column_number_ + 1} has no corresponding bracket")
                print(line_)
                print(f'{template}\n')

        stack = []
        for line_number, line in enumerate(lines):
            for column_number, char in enumerate(line):
                if char in brackets:
                    bracket_item = {
                        'line_number': line_number,
                        'column_number': column_number,
                        'line': line,
                        'val': char
                    }
                    if char in left_brackets:
                        stack.append(bracket_item)
                    elif char in right_brackets:
                        if len(stack) > 0 and stack[len(stack) - 1]['val'] == bracket_match[char]:
                            stack.pop()
                        else:
                            handle_bracket_match_error_log(bracket_item)

        for bracket_item in stack:
            handle_bracket_match_error_log(bracket_item)

    @staticmethod
    def check_constant_value(lines: List[AnyStr], log: bool = True):
        """
        检查常量数值
        :param lines:
        :param log:
        :return:
        """
        t = Tokenizer()
        variables = set(t.separate_variables(lines))
        for line_number, line in enumerate(lines):
            constants = [
                item for item in t.parse_lines([line])
                if item not in variables and is_user_defined(item)
            ]
            for constant in constants:
                if not re.match(r'^[-+]?[0-9]*\.?[0-9]+$', constant) and log:
                    idx = line.index(constant)
                    template = f"{' ' * idx}{'~' * len(constant)}"
                    print(
                        f"[Error] The constant '{constant}' at line {line_number + 1} position {idx + 1} is an illegal value")
                    print(line)
                    print(f'{template}\n')
